# PhotoAlbumsScreen - This screen contains a list of photograph albums
# Copyright (C) 2007 Lauri Taimila
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2007, Lauri Taimila"
__author__ = "Lauri Taimila <lauri@taimila.com>"

import os
import gtk
import pango
import gobject
import clutter

# Widgets
from frontend.gui.widgets.text_menu import TextMenu
from frontend.gui.widgets.text_menu_item import TextMenuItem
from frontend.gui.widgets.list_indicator import ListIndicator

from frontend.gui.screens.screen import Screen
from frontend.gui.transitions.transition import Transition
from frontend.gui.screens.photographs_screen import PhotographsScreen

class PhotoAlbumsScreen(Screen):
    """
    PhotoAlbumsScreen
    
    This screen contains a list of photo albums. This screen is used for
    selecting album from album list. Screen also displays preview for every
    album.
    """
    
    def __init__(self, ui, frontend):
        """
        Initialize screen
        
        @param frontend: FrontendClient object
        @param theme: Theme object
        """
        self.ui = ui
        self.frontend = frontend
        self.theme = frontend.getTheme()
        self.group = clutter.Group()
        self.library = frontend.get_image_library()
        self.timeout = None # Timeout key (this is used when scrolling menu)
        
        # Screen Title (Displayed at the bottom left corner)
        screen_title = clutter.Label()
        screen_title.set_font_name(self.theme.getFont() + ' 100px')
        screen_title.set_color(clutter.Color(0xff, 0xff, 0xff, 0x11))
        screen_title.set_text("Photographs")
        screen_title.set_name("screen_title")
        (screen_title_width, screen_title_height) = screen_title.get_size()
        screen_title_x = 0
        screen_title_y = ui.STAGE_HEIGHT - screen_title_height + 20
        screen_title.set_position(screen_title_x, screen_title_y)
        screen_title.show()
        self.group.add(screen_title)
        
        if self.library.get_number_of_images() == 0:
            self._create_no_photos_information()
        else:
            # Album preview group
            self.preview = clutter.Group()
            self.preview.set_position(ui.get_abs_x_pos(0.07), 150)
            self.preview.show()
            self.group.add(self.preview)
            
            # Albums Title (over album list)
            albums_title = clutter.Label()
            albums_title.set_font_name(self.theme.getFont() + ' 32px')
            albums_title.set_color(self.theme.getColor("title"))
            albums_title.set_text("Album")
            albums_title.set_position(740, 150)
            albums_title.show()
            self.group.add(albums_title)
            
            # Photos Title (over album list)
            albums_title = clutter.Label()
            albums_title.set_font_name(self.theme.getFont() + ' 32px')
            albums_title.set_color(self.theme.getColor("title"))
            albums_title.set_text("Photos")
            albums_title.set_position(1180, 150)
            albums_title.show()
            self.group.add(albums_title)
            
            #self.__create_view_menu()
            self._create_album_menu()
            self._create_list_indicator()
        
        self.group.show()
        ui.getStage().add(self.group)
    
    def _create_no_photos_information(self):
        """
        Create textures and labels for information screen. This is displayed
        instead of album list if there are no photos available. This screen helps
        users to add new photographs to the system.
        """
        # Create warning icon
        warning = gtk.gdk.pixbuf_new_from_file(self.theme.getImage("warning_icon"))
        warning_icon = clutter.Texture()
        warning_icon.set_pixbuf(warning)
        warning_icon.set_position(383,210)
        warning_icon.show()
        self.group.add(warning_icon)
    
        # Create warning title
        info_title = clutter.Label()
        info_title.set_font_name(self.theme.getFont() + " 48px")
        info_title.set_text("No photographs available!")
        info_title.set_position(460,210)
        info_title.set_color((255,255,255,255))
        info_title.show()
        self.group.add(info_title)
        
        # Create warning help text
        info = clutter.Label()
        info.set_text("""There are no indexed photographs in the Entertainer media library. To add photographs, click on 'content' button on toolbar and open 'images' tab. Now click on 'add' button and select some folders which contains image files.""")
        info.set_size(690,450)
        info.set_position(390,330)
        info.set_line_wrap(True)
        info.set_font_name(self.theme.getFont() + " 32px")
        info.set_color(self.theme.getColor("menuitem_inactive"))
        info.show()
        self.group.add(info)
    
#    def _create_view_menu(self):
#        """Create horizontal menu that allows user to change view."""
#        self.tabs = TabsMenu(self.theme, 
#                               self.frontend.get_configuration().show_effects())
#        self.tabs.set_position(130, 50)
#        self.tabs.set_item_size(600,50)
#        
#        tab1 = MenuItem(600, 50, self.theme, "List view")
#        tab1.set_userdata("list")
#        
#        tab2 = MenuItem(600, 50, self.theme, "Timeline view")
#        tab2.set_userdata("timeline")
#
#        self.tabs.add_actor(tab1)
#        self.tabs.add_actor(tab2)
#        self.tabs.show()
#        self.group.add(self.tabs)
    
    def _create_album_menu(self):
        """
        Create ImageAlbum-menu. This menu contains list of albums. It also 
        displays number of photographs per album.
        """        
        self.menu = TextMenu(self.theme, 
                               self.frontend.get_configuration().show_effects())
        self.menu.set_row_count(1)
        self.menu.set_position(720, 200)
        self.menu.show()

        # Create menu items based on ImageLibrary
        albums = self.library.get_albums()
        for album in albums:        
            nro_of_photos = album.get_number_of_images()
            if nro_of_photos == 0: # Skip empty albums
                continue
            else:
                nro_of_photos = str(nro_of_photos)
            item = TextMenuItem(570, 60, self.theme, album.get_title(), nro_of_photos)
            item.set_userdata(album) #FIXME: Should we use URLs as KEYS?
            self.menu.add_actor(item)
        
        self.group.add(self.menu)
        self._update_album_preview(self.menu.get_current_menuitem().get_userdata())
    
    def _create_album_preview(self, album):
        """
        Create a clutter.Group that contains album preview actors.
        """
        group = clutter.Group()
        group.set_position(self.ui.get_abs_x_pos(0.07), 150)
        
        rect = clutter.Rectangle()
        rect.set_size(550, 550)
        rect.set_color(clutter.Color(255,255,255,32))
        rect.show()
        #group.add(rect)
        
        # Preview images
        images = album.get_preview_images(3)
        self.preview_textures = []

        for image in images:
            texture = clutter.Texture()
            buffer = gtk.gdk.pixbuf_new_from_file(image.get_thumbnail_url())
            texture.set_pixbuf(buffer)
            texture.set_opacity(0)
            
            # Resize and center preview texture
            MAX_W = 550
            MAX_H = 430
            w, h = texture.get_size()
            x_ratio = MAX_W / float(w)
            y_ratio = MAX_H / float(h)
            if x_ratio > y_ratio:
                texture.set_scale(MAX_H /float(h), MAX_H / float(h))
                new_width = int(w * (MAX_H / float(h)))
                new_x = int((MAX_W - new_width) / 2.0)
                texture.set_position(new_x, 0)
            else:
                texture.set_scale(MAX_W / float(w), MAX_W / float(w))
                new_height = int(h * (MAX_W/ float(w)))
                new_y = int((MAX_H - new_height) / 2.0)
                texture.set_position(0, new_y)
            
            texture.show()
            self.preview_textures.append(texture)
            group.add(texture)
        self.preview_textures[0].set_opacity(255)

        # Album Title
        title = clutter.Label()
        title.set_font_name(self.theme.getFont() + ' 28px')
        title.set_color(self.theme.getColor("title"))
        title.set_text(album.get_title())
        title.set_position(0, 450)
        title.set_width(550)
        title.set_ellipsize(pango.ELLIPSIZE_END)
        title.show()
        group.add(title)
        
        # Album description
        desc = clutter.Label()
        desc.set_font_name(self.theme.getFont() + ' 24px')
        desc.set_color(self.theme.getColor("subtitle"))
        desc.set_text(album.get_description())
        desc.set_width(550)
        desc.set_position(0, 490)
        desc.set_ellipsize(pango.ELLIPSIZE_END) #FIXME: This should be fixed to show muliline
        desc.show()
        group.add(desc)
        
        return group
    
    def _update_album_preview(self, album):
        """
        Update album preview. Display preview images from the current album.
        @param album: Currently selected album in menu
        """
        try:
            gobject.source_remove(self.preview_fade) 
        except:
            pass # If there is no image switcher enabled that's ok
               
        new = self._create_album_preview(album)
        
        if self.frontend.get_configuration().show_effects():
            old = self.preview
            new.set_opacity(0)
            new.show()
            self.preview = new
            self.group.add(self.preview)
            
            #Fade out timeline
            self.timeline1 = clutter.Timeline(10, 26)
            self.alpha1 = clutter.Alpha(self.timeline1, clutter.smoothstep_inc_func)
            self.out_opacity = clutter.BehaviourOpacity(255, 0, self.alpha1)
            self.out_opacity.apply(old)
            #self.out_scale = clutter.BehaviourScale(self.alpha1, 1.0, 0.8, clutter.GRAVITY_CENTER)
            #self.out_scale.apply(old)
            #self.out_path = clutter.BehaviourPath(self.alpha1, 
            #                                      ((old.get_x(), old.get_y()),
            #                                      (old.get_x(), old.get_y() + 50)))
            #self.out_path.apply(old)
            
            self.timeline1.connect('completed', self._change_preview_timeline_ended, old)
            
            # Fade in timeline
            self.timeline2 = clutter.Timeline(10, 26)
            self.alpha2 = clutter.Alpha(self.timeline2, clutter.smoothstep_inc_func)
            self.in_opacity = clutter.BehaviourOpacity(0, 255, self.alpha2)
            self.in_opacity.apply(new)
            #self.in_scale = clutter.BehaviourScale(self.alpha2, 1.2, 1.0, clutter.GRAVITY_CENTER)
            #self.in_scale.apply(new)
            #self.in_path = clutter.BehaviourPath(self.alpha2,
            #                                     ((new.get_x(), new.get_y() - 50),
            #                                     (new.get_x(), new.get_y())))
            #self.in_path.apply(new)
            
            # Start animation
            self.timeline1.start()
            self.timeline2.start()
            
        else:
            # Do it without animation
            if self.preview is not None:
                self.group.remove(self.preview)
            self.preview = new
            #self.preview.rotate_y(20,0,0)
            self.group.add(self.preview)
            self.preview.show()
            
        self.preview_fade = gobject.timeout_add(6000, self._change_preview_image)
        return False # see gobject.timeout_add() doc

    def _change_preview_timeline_ended(self, timeline, group):
        """
        This is a callback function for preview updates. This is called when
        transition effect is finished. This mehtod removes old preview group
        from the stage.
        """
        self.group.remove(group)

    def _change_preview_image(self):
        """
        Run a timeline that crossfades preview images. This method is a callback
        that is called every 4 seconds.
        """       
        if self.frontend.get_configuration().show_effects():
            #Fade out timeline
            self.fade_out = clutter.Timeline(40, 26)
            self.alpha_out = clutter.Alpha(self.fade_out, clutter.smoothstep_inc_func)
            self.out_behaviour = clutter.BehaviourOpacity(255, 0, self.alpha_out)
            self.out_behaviour.apply(self.preview_textures[0])
            
            # Fade in timeline
            self.fade_in = clutter.Timeline(40, 26)
            self.alpha_in = clutter.Alpha(self.fade_in, clutter.smoothstep_inc_func)
            self.in_behaviour = clutter.BehaviourOpacity(0, 255, self.alpha_in)
            self.in_behaviour.apply(self.preview_textures[1])
            
            # Start animation
            self.fade_out.start()
            self.fade_in.start()
        else:
            self.preview_textures[0].set_opacity(0)
            self.preview_textures[1].set_opacity(255)
        
        # Scroll images
        self.preview_textures = self.preview_textures[1:] + self.preview_textures[:1]
        
        return True
    
    def _create_list_indicator(self):
        """
        Create list indicator for albums list.
        """
        self.li = ListIndicator(200,34, self.theme, ListIndicator.VERTICAL)
        li_y = self.frontend.getUserInterface().STAGE_HEIGHT - self.li.get_height() - 30
        li_x = 1270 - self.li.get_width()
        self.li.set_position(li_x,li_y)
        self.li.set_delimiter(" | ")
        self.li.show()
        self.li.set_maximum(self.menu.get_number_of_items())
        self.li.set_current(1)
        self.group.add(self.li)
    
    def get_group(self):
        """
        Get all actors of the screen.
        @return clutter.Group 
        """
        return self.group

    def get_type(self):
        """Return screen type."""
        return Screen.NORMAL
    
    def get_name(self):
        """Return screen name (human readble)"""
        return "PhotoAlbumScreen"
    
    def handle_key_press_event(self, event):   
        """ Handle key pressed event. This can be transmitted to the widgets."""
        if event.keyval == clutter.keysyms.Up:
            self.menu.move(TextMenu.UP)
            self.li.set_current(self.menu.get_current_postition() + 1)
            # 500ms timeout before preview is updated (fast scrolling)
            if self.timeout is not None:
                gobject.source_remove(self.timeout) 
            self.timeout = gobject.timeout_add(500, self._update_album_preview, self.menu.get_current_menuitem().get_userdata())
            #self._update_album_preview(self.menu.get_current_menuitem().get_userdata())
        elif event.keyval == clutter.keysyms.Down:
            self.menu.move(TextMenu.DOWN)
            self.li.set_current(self.menu.get_current_postition() + 1)
            # 500ms timeout before preview is updated (fast scrolling)
            if self.timeout is not None:
                gobject.source_remove(self.timeout) 
            self.timeout = gobject.timeout_add(500, self._update_album_preview, self.menu.get_current_menuitem().get_userdata())
            #self._update_album_preview(self.menu.get_current_menuitem().get_userdata())
            
        elif event.keyval == clutter.keysyms.Return:
            album = self.menu.get_current_menuitem().get_userdata()
            userdata = [album.get_title(), album.get_images()]
            self.ui.changeScreen(self.ui.createScreen("photographs", userdata), Transition.FORWARD)
